package org.jef.socket.aspect;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.jef.core.util.SpringContextHolder;
import org.jef.core.util.UnsafeUtils;
import org.jef.socket.annotation.AsyncWs;
import org.jef.socket.annotation.SyncWs;
import org.jef.socket.event.EventMsg;
import org.jef.socket.event.WebSocketAsyncEvent;
import org.jef.socket.event.WebSocketSyncEvent;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/**
 * @version 1.0
 * @description: 异步websocket处理
 * @author: ChengZhi 0336
 * @create: 2024-08-23 14:51
 **/
@Aspect
@Slf4j
@Component
public class WebSocketAspect {

    private static final String DEFALUT_ROOMID = "default";

    @Around("@annotation(asyncWs)")
    @SneakyThrows
    public Object aroundAsync(ProceedingJoinPoint point, AsyncWs asyncWs) {

        String value = asyncWs.value();
        String roomKey = asyncWs.roomKey();
        String sign = asyncWs.sign();
        if (roomKey != null && !roomKey.equals("")) {
            return logicalProcessing(point, value, roomKey, sign);
        }
        return logicalProcessing(point, value, sign);
    }

    @Around("@annotation(syncWs)")
    @SneakyThrows
    public Object aroundSync(ProceedingJoinPoint point, SyncWs syncWs) {

        String value = syncWs.value();
        String roomKey = syncWs.roomKey();
        String sign = syncWs.sign();
        if (roomKey != null && !roomKey.equals("")) {
            return logicalProcessing(point, value, roomKey, sign);
        }
        return logicalProcessing(point, value, sign);
    }

    /**
     * 逻辑处理
     * @param point
     * @param value
     * @return
     */
    @SneakyThrows
    private Object logicalProcessing(ProceedingJoinPoint point, String value, String sign) {

        return logicalProcessing(point, value, DEFALUT_ROOMID, sign);
    }

    /**
     * 逻辑处理： 拦截方法入参参数，优先从参数中获取参数名为roomKey的值，如果参数中不存在就从复杂类型的属性中获取字段名为roomkey的值， ，
     * @return
     */
    @SneakyThrows
    private Object logicalProcessing(ProceedingJoinPoint point, String value, String roomId, String sign) {
        String strClassName = point.getTarget().getClass().getName();
        Signature signature = point.getSignature();
        String strMethodName = signature.getName();
        log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);

        MethodSignature methodSignature = (MethodSignature) signature;
        String[] parameterNames = methodSignature.getParameterNames();
        boolean falg = false;  // 参数中是否存在room标识
        for (int i = 0; i < parameterNames.length; i++) {
            String parameterName = parameterNames[i];
            if (sign.equals(parameterName)) {
                // 说明方法入参中含有目标关键字,直接取值
                Object arg = point.getArgs()[i];
                value = value + "_" + arg.toString();
                falg = true;
            }

            if (falg) {
                break;
            }
        }

        Object[] args = point.getArgs();
        for (Object arg : args) {
            if (falg) {
                break;
            }
            if (arg == null) {
                continue;
            }
            try {
                Class<?> aClass = arg.getClass();
                if (!aClass.isPrimitive()) {
                    Field field = arg.getClass().getDeclaredField(sign);
                    Object o = UnsafeUtils.forcedGetValue(arg, field);
                    value = value + "_" + o.toString();
                    falg = true;
                }
            } catch (Exception e) {
                log.warn("get roomId error", e);
            }
        }
        EventMsg eventMsg = new EventMsg();
        eventMsg.setMsg(value);
        eventMsg.setRoomId(DEFALUT_ROOMID);
        Object obj;
        try {
            obj = point.proceed();
        } catch (Exception e) {
            throw e;
        } finally {
            SpringContextHolder.publishEvent(new WebSocketAsyncEvent(eventMsg));
        }
        return obj;
    }
}
